/*
 * Tic-Tac-Toe - Turn-based strategy game
 * Copyright 2008, 2009 Shayne Riley and Paul Maseberg
 *
 * Tic-Tac-Toe is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tic-Tac-Toe is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tic-Tac-Toe.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <shayner at gmail dot com>
 */

#ifndef GRAPHICS_H_
#define GRAPHICS_H_

#include <SDL/SDL.h>
#ifdef _WIN32
#include <SDL/SDL_OpenGL.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL_image.h>
#include <FTGL/ftgl.h>
#include <list>

/**
 * This class is completely not thread safe. Don't even think about it, bub.
 */
class Graphics
{
public:

    /**
     *	Control state for the mouse. There can be multiple ones per frame, since
     *  the mouse can update multiple times before a frame is drawn.
     */
    struct Mouse
    {
        /** True if the mouse is located on the app somewhere. */
        bool mouseOn;
        /** Horizontal location of the mouse, 0 = left. */
        Uint16 mouseX;
        /** Vertical location of the mouse, 0 = bottom. (Yes, I know. Quiet.) */
        Uint16 mouseY;
        /** State of primary mouse button. True means pressed down.*/
        bool button0;
    };

    /**
     * The valueable texture information. This one is for OpenGL it seems.
     */
    struct Texture
    {
        GLuint name;
        GLint width;
        GLint height;
    };

    Graphics();
    virtual ~Graphics();

    bool operational;
    bool sdlInited;
    bool shouldQuit;
    bool showFPS; ///< If true, show the FPS onscreen.

    /**
     * The state of the mouse from the user. Because it is possible
     * that the user can click and move the mouse multiple times during a single
     * frame (if the graphics renderer is slow enough), it is presented as a
     * list. It is also possible that the user did nothing, in which case no
     * controls will be in the list.
     *
     * The state is grabbed at the beginning of the frame drawing. All
     * events that occur after that point will not be registered in the game
     * until the beginning of the next frame. So there.
     *
     * Also: I know this isn't the best solution out there. Simmer donna!
     */
    std::list<Mouse> controls;

    /**
     * The milliseconds since the start of the game that the drawing of this
     * frame began.
     */
    uint32_t frameMillis;

    /**
     * Properly shuts down the graphics system. You'd think that this would
     * be automatically called in the destructor but you'd be wrong. Otherwise
     * it would be shutting down all over the place, and we can't have that.
     */
    void destroy();

    /**
     * Called at the end of all the drawing operations, so that it will be
     * displayed on screen to the user. The double buffer is flipped.
     */
    void endFrame();

    /**
     * Draws the game. Well, not ALL of the game, just the background and
     * a couple of other things.
     */
    bool drawGame();

    /**
     * Draws a two-dimensional GL Texture onto the framebuffer with no resizing
     * or rotation, or any other special properties.
     * @param texture	The texture to draw.
     * @param leftX     The texture's left side starts being drawn here.
     * @param lowerY    The texture's lower side starts being drawn here.
     */
    void drawTexture(Texture *texture, GLint leftX, GLint lowerY);

    /**
     * Draws a two-dimesional GL Texture onto the framebuffer, but allows
     * 	rotation about the center of the texture.
     * @param texture	The texture to draw.
     * @param centerX	The center of the texture is drawn here.
     * @param centerY	The center of the texture is drawn here.
     * @param angleDeg	The angle of counter-clockwise rotation in degrees.
     */
    void drawTexture(Texture *texture, GLint centerX, GLint centerY,
            float angleDeg);

    /**
     * Draws text to the framebuffer.
     *
     * @param text  The zero-terminated string to print.
     * @param leftX The left-side of the text to start outputting English text
     *  left-to-right.
     * @param baselineY The baseline position for the text (a.k.a. the bottom).
     */
    void drawText(const char *text, GLint leftX, GLint baselineY);

    /**
     * Draws text in the center of the bounding box. (The text center is based
     * on the full height of the font set, so the baseline won't be shifting
     * around any. No worries.)
     * @param text  The text to print.
     * @param leftX The left-side of the box to print inside.
     * @param lowerY The lower-side of the box.
     * @param width The width of the box.
     * @param height The height of the box.
     */
    void drawText(const char *text, GLint leftX, GLint lowerY, GLint width,
            GLint height);

    /**
     * Draws a two-dimesional GL Texture onto the framebuffer, but allows
     * 	rotation about the center of the texture.
     * @param texture	The texture to draw.
     * @param centerX	The center of the texture is drawn here.
     * @param centerY	The center of the texture is drawn here.
     * @param width		The width of the texture before rotation is applied.
     * @param height	The width of the texture before rotation is applied.
     * @param angleDeg	The angle of counter-clockwise rotation in degrees.
     */
    void drawTexture(Texture *texture, GLint centerX, GLint centerY,
            GLint width, GLint height, float angleDeg);

    /**
     * Draws a rectangle outline.
     * @param leftX The lower-left horizontal coordinate. Origin lower-left.
     * @param lowerY The lower-left vertical coordinate.
     * @param width The width of the rectangle in pixels. Can be negative, sure.
     * @param height The height of the rectangle in pixels.
     */
    void drawRect(GLint leftX, GLint lowerY, GLint width, GLint height);

    /**
     * Draws a filled rectangle.
     * @param leftX The lower-left horizontal coordinate. Origin lower-left.
     * @param lowerY The lower-left vertical coordinate.
     * @param width The width of the rectangle in pixels. Can be negative, sure.
     * @param height The height of the rectangle in pixels.
     */
    void drawRectFill(GLint leftX, GLint lowerY, GLint width, GLint height);

    /**
     * Set the thickness of the shapes drawn. Doesn't affect textures or text.
     * @param thickness The thickness to set, in pixels.
     */
    void setThickness(GLfloat thickness);

    /**
     * Sets the color used for any shapes, text, or textures.
     *
     * Fully white would be 1, 1, 1, 1, and is recommended for textures.
     * Black is 0, 0, 0, 1.
     * Half-transparent orange is 1, 0.5, 0, 0.5.
     * @param red   Amount of red in the color. 0 none, 1 all.
     * @param green Amount of green in the color. 0 none, 1 all.
     * @param blue  Amount of blue in the color. 0 none, 1 all.
     * @param alpha Amount of opacity in the color. 0 invisible, 1 (default)
     *  fully opaque.
     */
    void setColor(float red, float green, float blue, float alpha = 1.0f);

    /**
     * Sets the size of the font output.
     * @param size the face size in points (1/72 inch).
     */
    void setFontSize(uint32_t size);

    Texture textureO;
    Texture textureX;
    Texture textureLine;
    Texture textureBack;
    Texture textureYou;
    Texture textureWin;
    Texture textureLose;
    Texture textureCat;
    Texture textureTitle;
    Texture texturePezad;

private:
    Mouse lastControl; /**< The last user input into the game. */
    FTGLPixmapFont font;
    size_t frameCount; ///< In case the frames are drawn faster than 1000 fps.

    /**
     * Appends a mouse state loaded in from the event queue. The parts of the
     * mouse state cared about are the parameters.
     * @param mouseOn true if the mouse is on the window and the window is in
     *  focus.
     * @param mouseX the horizontal position of the mouse, in distance from
     *  the left side of the screen.
     * @param mouseY the vertical position of the mouse, in distance from the
     *  bottom side of the screen.
     * @param button0 true if the primary mouse button is pressed.
     */
    void addMouse(bool mouseOn, Uint16 mouseX, Uint16 mouseY, bool button0);

    /**
     * Munches on the event queue. Interprets the mouse event into a state
     * that can be read by the rest of the game. The quit event will shut down
     * the game.
     */
    void consumeEvents();

    /**
     * Loads an image from the file and places it into GL managed memory.
     * @param inFile	The 0-terminated string ASCII name of the file.
     * @param outTextureName	The OpenGL reference to the texture holding the
     *  image is placed here. If there was an error loading the image, the name
     *  of the texture, and its width and size, will all be 0.
     * @return True on successful load; or false, outTextureName is 0, and
     *  outputs an error.
     */
    bool loadTexture(const char *inFile, Texture *outTexture);

    /**
     * @returns true if successfully created all graphics for the game, or
     * outputs an error and returns false if not.
     */
    bool initGraphics();

    /**
     * @returns true if successfully created an OpenGL-based window, or outputs
     * an error and returns false if not.
     */
    bool initGL();

    /**
     * @returns true if SDL was initialized correctly, or outputs an error and
     * 	returns false if not.
     */
    bool initSDL();
};

#endif /* GRAPHICS_H_ */
